package com.utils;

import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;

public class RSA
{
  public static final String KEY_ALGORITHM = "RSA";
  public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
  private static final String PUBLIC_KEY = "RSAPublicKey";
  private static final String PRIVATE_KEY = "RSAPrivateKey";
  
  public static String sign(byte[] data, String privateKey)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(privateKey);
    
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
    PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
    
    Signature signature = Signature.getInstance("MD5withRSA");
    signature.initSign(priKey);
    signature.update(data);
    
    return Encodes.encodeBase64(signature.sign());
  }
  
  public static boolean verify(byte[] data, String publicKey, String sign)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(publicKey);
    
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
    
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
    PublicKey pubKey = keyFactory.generatePublic(keySpec);
    
    Signature signature = Signature.getInstance("MD5withRSA");
    signature.initVerify(pubKey);
    signature.update(data);
    
    return signature.verify(Encodes.decodeBase64(sign));
  }
  
  public static byte[] decryptByPrivateKey(byte[] data, String key)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(key);
    
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
    
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(2, privateKey);
    
    return cipher.doFinal(data);
  }
  
  public static byte[] decryptByPublicKey(byte[] data, String key)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(key);
    
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    Key publicKey = keyFactory.generatePublic(x509KeySpec);
    
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(2, publicKey);
    
    return cipher.doFinal(data);
  }
  
  public static byte[] encryptByPublicKey(byte[] data, String key)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(key);
    
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    Key publicKey = keyFactory.generatePublic(x509KeySpec);
    
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(1, publicKey);
    
    return cipher.doFinal(data);
  }
  
  public static byte[] encryptByPrivateKey(byte[] data, String key)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(key);
    
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
    
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(1, privateKey);
    
    return cipher.doFinal(data);
  }
  
  public static String getPrivateKey(Map<String, Object> keyMap)
    throws Exception
  {
    Key key = (Key)keyMap.get("RSAPrivateKey");
    
    return Encodes.encodeBase64(key.getEncoded());
  }
  
  public static String getPublicKey(Map<String, Object> keyMap)
    throws Exception
  {
    Key key = (Key)keyMap.get("RSAPublicKey");
    
    return Encodes.encodeBase64(key.getEncoded());
  }
  
  public static Map<String, Object> initKey()
    throws Exception
  {
    KeyPairGenerator keyPairGen = 
      KeyPairGenerator.getInstance("RSA");
    keyPairGen.initialize(1024);
    
    KeyPair keyPair = keyPairGen.generateKeyPair();
    
    RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
    
    RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
    
    Map<String, Object> keyMap = new HashMap(2);
    
    keyMap.put("RSAPublicKey", publicKey);
    keyMap.put("RSAPrivateKey", privateKey);
    return keyMap;
  }
  
  public static PublicKey getPublicKey(String key)
    throws Exception
  {
    byte[] keyBytes = Encodes.decodeBase64(key);
    
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(x509KeySpec);
  }
  
  public static String getRSAPublicKeyModulusToHex(String publicKey)
    throws Exception
  {
    RSAPublicKey key = (RSAPublicKey)getPublicKey(publicKey);
    return key.getModulus().toString(16);
  }
  
  public static String getRSAPublicKeyExponentToHex(String publicKey)
    throws Exception
  {
    RSAPublicKey key = (RSAPublicKey)getPublicKey(publicKey);
    return key.getPublicExponent().toString(16);
  }
}
